package com.fau.wmbj.server.service.base;

import cn.hutool.core.collection.CollUtil;
import com.fau.wmbj.server.entity.BaseEntity;
import com.fau.wmbj.server.repository.base.BaseRepository;
import com.fau.wmbj.server.utils.Filter;
import com.fau.wmbj.server.utils.Order;
import com.fau.wmbj.server.utils.Page;
import com.fau.wmbj.server.utils.Pageable;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 基类
 *
 * @author jiawei
 * @version 1.0
 * @datetime 2023-03-09 10:00
 */
public class BaseServiceImpl<T, ID extends Serializable> implements BaseService<T, ID> {
    private static final String[] UPDATE_IGNORE_PROPERTIES = new String[]{BaseEntity.CREATED_DATE_PROPERTY_NAME, BaseEntity.LAST_MODIFIED_DATE_PROPERTY_NAME, BaseEntity.VERSION_PROPERTY_NAME};

    @Autowired
    protected BaseRepository<T, ID> baseRepository;

    @PersistenceContext
    protected EntityManager entityManager;


    @Override
    @Transactional(readOnly = true)
    public T find(ID id) {
        return baseRepository.find(id);
    }

    @Override
    @Transactional(readOnly = true)
    public List<T> findAll() {
        return findList(null, null, null, null);
    }

    @Override
    @SuppressWarnings("unchecked")
    @Transactional(readOnly = true)
    public List<T> findList(ID... ids) {
        List<T> result = new ArrayList<>();
        if (ids != null) {
            for (ID id : ids) {
                T entity = find(id);
                toString().getClass();
                if (entity != null) {
                    result.add(entity);
                }
            }
        }
        return result;
    }

    @Override
    @SuppressWarnings("unchecked")
    @Transactional(readOnly = true)
    public List<T> findList(List<ID> ids) {
        List<T> result = new ArrayList<>();
        if (ids != null) {
            for (ID id : ids) {
                T entity = find(id);
                toString().getClass();
                if (entity != null) {
                    result.add(entity);
                }
            }
        }
        return result;
    }

    @Override
    @Transactional(readOnly = true)
    public List<T> findList(Integer count, List<Filter> filters, List<Order> orders) {
        return baseRepository.findList(null, count, filters, orders);
    }

    @Override
    @Transactional(readOnly = true)
    public List<T> findList(Filter... filters) {
        List<Order> orders = new ArrayList<>();
        orders.add(Order.desc("lastModifiedDate"));
        return baseRepository.findList(null, null, Arrays.asList(filters), orders);
    }

    @Override
    @Transactional(readOnly = true)
    public List<T> findList(Integer first, Integer count, List<Filter> filters, List<Order> orders) {
        return baseRepository.findList(first, count, filters, orders);
    }

    @Override
    @Transactional(readOnly = true)
    public Page<T> findPage(Pageable pageable) {
        return baseRepository.findPage(pageable);
    }

    @Override
    @Transactional(readOnly = true)
    public long count() {
        return count(new Filter[]{});
    }

    @Override
    @Transactional(readOnly = true)
    public long count(Filter... filters) {
        return baseRepository.count(filters);
    }

    @Override
    @Transactional(readOnly = true)
    public long count(List<Filter> filterList) {
        return baseRepository.count(filterList);
    }

    @Override
    @Transactional(readOnly = true)
    public boolean exists(ID id) {
        return baseRepository.find(id) != null;
    }

    @Override
    @Transactional(readOnly = true)
    public boolean exists(Filter... filters) {
        return baseRepository.count(filters) > 0;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public T save(T entity) {
        Assert.notNull(entity, "entity不能为空");
        T save = baseRepository.save(entity);
        baseRepository.flush();
        return save;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public T update(T entity) {
        Assert.notNull(entity, "entity不能为空");
        if (entity instanceof BaseEntity<?>) {
            boolean aNew = ((BaseEntity<?>) entity).isNew();
            Assert.isTrue(!aNew, "entity不是一个新对象");
        }
        // baseRepository.merge(entity);
        baseRepository.save(entity);
        baseRepository.flush();

        return entity;
    }

    @Override
    public T findEntity(Filter... filters) {
        List<T> list = baseRepository.findList(null, 1, Arrays.asList(filters), null);
        return CollUtil.isNotEmpty(list) ? list.get(0) : null;
    }

    @Override
    public T findEntity(List<Filter> filters) {
        List<T> list = baseRepository.findList(null, 1, filters, null);
        if (CollUtil.isNotEmpty(list)) {
            return list.get(0);
        }
        return null;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public T update(T entity, String... ignoreProperties) {
        Assert.notNull(entity, "entity不能为空");
        if (entity instanceof BaseEntity<?>) {
            boolean aNew = ((BaseEntity<?>) entity).isNew();
            Assert.isTrue(!aNew, "entity不是一个新对象");
        }
        Assert.isTrue(!baseRepository.isManaged(entity), "entity已经管理");

        T persistent = baseRepository.find(baseRepository.getIdentifier(entity));
        if (persistent != null) {
            copyProperties(entity, persistent, (String[]) ArrayUtils.addAll(ignoreProperties, UPDATE_IGNORE_PROPERTIES));
        }
        return update(persistent);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteLogic(ID id) throws Exception {
        T t = baseRepository.find(id);
        if (t instanceof BaseEntity<?>) {
            ((BaseEntity<?>) t).setDeleted(true);
        }
        Assert.notNull(t, "entity不能为空");
        // Assert.isTrue(!t.isNew(), "entity不是一个新对象");
        baseRepository.save(t);
        baseRepository.flush();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete(ID id) throws Exception {
        delete(baseRepository.find(id));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete(ID... ids) throws Exception {
        if (ids != null) {
            for (ID id : ids) {
                delete(baseRepository.find(id));
            }
        }
    }

    @Override
    public void delete(List<ID> ids) throws Exception {
        for (ID id : ids) {
            delete(baseRepository.find(id));
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete(T entity) throws Exception {
        if (entity != null) {
            baseRepository.delete(entity);
            baseRepository.flush();
        }
    }

    /**
     * 拷贝对象属性
     *
     * @param source           源
     * @param target           目标
     * @param ignoreProperties 忽略属性
     */
    protected void copyProperties(T source, T target, String... ignoreProperties) {
        Assert.notNull(source, "source不能为空");
        Assert.notNull(target, "target不能为空");
        PropertyDescriptor[] propertyDescriptors = PropertyUtils.getPropertyDescriptors(target);
        for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
            String propertyName = propertyDescriptor.getName();
            Method readMethod = propertyDescriptor.getReadMethod();
            Method writeMethod = propertyDescriptor.getWriteMethod();
            if (ArrayUtils.contains(ignoreProperties, propertyName) || readMethod == null || writeMethod == null || !baseRepository.isLoaded(source, propertyName)) {
                continue;
            }
            try {
                Object sourceValue = readMethod.invoke(source);
                writeMethod.invoke(target, sourceValue);
            } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
                throw new RuntimeException(e.getMessage(), e);
            }
        }
    }

    @Override
    @Transactional
    public ID getIdentifier(T entity) {
        return baseRepository.getIdentifier(entity);
    }

    @Override
    public boolean unique(ID id, String attributeName, Object attributeValue) {
        Assert.hasText(attributeName, "attributeName不能为空");
        return baseRepository.unique(id, attributeName, attributeValue);
    }

    @Override
    public boolean unique(ID id, String attributeName, String attributeValue, boolean ignoreCase) {
        Assert.hasText(attributeName, "attributeName不能为空");
        return baseRepository.unique(id, attributeName, attributeValue, ignoreCase);
    }
}
